home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Applications / HexEdit 1.21 / ~Project / Source / EditRoutines.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-08-03  |  18.1 KB  |  785 lines  |  [TEXT/CWIE]

  1. /*********************************************************************
  2.  * EditRoutines.c
  3.  *
  4.  * Implements a linked-list / virtual memory scheme for hex editing
  5.  *
  6.  * HexEdit, a simple hex editor
  7.  * copyright 1993, Jim Bumgardner
  8.  *********************************************************************/
  9.  
  10. #include "HexEdit.h"
  11.  
  12. #include <ctype.h>
  13.  
  14. EditChunk    **gScrapChunk;
  15. short        gScrapCount;
  16. UndoRecord    gUndoRec, gRedoRec;
  17.  
  18. void LoadFile(EditWindowPtr dWin);
  19. void UnloadFile(EditWindowPtr dWin);
  20. EditChunk **NewChunk(long size, long addr, long filePos, short type);
  21. EditChunk **AppendChunk(EditChunk **list, EditChunk **chunk);
  22. void LoadChunk(EditWindowPtr dWin, EditChunk **cc);
  23. void UnloadLeastUsedChunk(EditWindowPtr dWin);
  24. void UnloadChunk(EditWindowPtr dWin, EditChunk    **cc, Boolean writeFlag);
  25.  
  26. // Assumes window has just been opened, file is open, fileSize field is correct
  27. void LoadFile(EditWindowPtr dWin)
  28. {
  29.     EditChunk    **nc;
  30.     long        count,chunkSize,pos;
  31.     count = dWin->fileSize;
  32.     pos = 0L;
  33.  
  34.     //LR - if empty fork, just create a chunk so we can insert data!
  35.  
  36.     if( !count )
  37.         count = 1;
  38.  
  39.     while (count)
  40.     {
  41.         if (count <= (MaxFileRAM - SlushRAM))
  42.             chunkSize = count;
  43.         else
  44.             chunkSize = (MaxFileRAM - SlushRAM);
  45.         count -= chunkSize;
  46.         nc = NewChunk(chunkSize,pos,pos, CT_Original);
  47.         dWin->firstChunk = AppendChunk(dWin->firstChunk, nc);
  48.         pos += chunkSize;
  49.     }
  50.     dWin->curChunk = dWin->firstChunk;
  51. }
  52.  
  53. void DisposeChunk(EditWindowPtr dWin, EditChunk **cc)
  54. {
  55.     if ((*cc)->loaded) {
  56.         if ((*cc)->loaded)
  57.             UnloadChunk(dWin,cc, false);
  58.         DisposeHandle((Handle) cc);
  59.     }
  60. }
  61.  
  62. void UnloadFile(EditWindowPtr dWin)
  63. {
  64.     EditChunk    **cc,**bc;
  65.     cc = dWin->firstChunk;
  66.     while (cc) {
  67.         bc = (*cc)->next;
  68.         DisposeChunk(dWin, cc);
  69.         cc = bc;
  70.     }
  71.     dWin->firstChunk = dWin->curChunk = NULL;
  72. }
  73.  
  74.  
  75. EditChunk **NewChunk(long size, long addr, long filePos, short type)
  76. {
  77.     EditChunk **nc;
  78.     nc = (EditChunk **) NewHandleClear(sizeof(EditChunk));
  79.     if (nc == NULL) {
  80.         ErrorAlert(ES_Caution, "Out of Memory");
  81.         return NULL;
  82.     }
  83.     (*nc)->type = type;
  84.     (*nc)->size = size;
  85.     (*nc)->addr = addr;
  86.     (*nc)->filePos = filePos;
  87.     (*nc)->lastCtr = -1;
  88.     if (type == CT_Unwritten) {
  89.         (*nc)->loaded = true;
  90.         (*nc)->allocSize = size;
  91.         (*nc)->data = NewHandleClear((*nc)->allocSize);
  92.         if ((*nc)->data == NULL) {
  93.             ErrorAlert(ES_Caution, "Out of Memory");
  94.             DisposeHandle((Handle) nc);
  95.             return NULL;
  96.         }
  97.     }
  98.     else {
  99.         (*nc)->loaded = false;
  100.         (*nc)->data = NULL;
  101.         (*nc)->allocSize = 0L;
  102.     }
  103.     return nc;
  104. }
  105.  
  106. EditChunk **AppendChunk(EditChunk **list, EditChunk **chunk)
  107. {
  108.     if (list) {
  109.         register EditChunk    **curChunk;
  110.         curChunk = list;
  111.         while ((*curChunk)->next)
  112.             curChunk = (*curChunk)->next;
  113.         (*curChunk)->next = chunk;
  114.         (*chunk)->prev = curChunk;
  115.         (*chunk)->next = NULL;
  116.     }
  117.     else {
  118.         list = chunk;
  119.         (*chunk)->next = (*chunk)->prev = NULL;
  120.     }
  121.     return list;
  122. }
  123.  
  124. void SetCurrentChunk(EditWindowPtr dWin, long addr)
  125. {
  126.     register EditChunk **cc;
  127.     cc = GetChunkByAddr(dWin,addr);
  128.     dWin->curChunk = cc;
  129. }
  130.  
  131. EditChunk **GetChunkByAddr(EditWindowPtr dWin, long addr)
  132. {
  133.     register EditChunk **cc;
  134.     if (dWin->curChunk && addr >= (*dWin->curChunk)->addr)
  135.         cc = dWin->curChunk;
  136.     else    // Otherwise, start from beginning of chain
  137.         cc = dWin->firstChunk;
  138.     while (cc)
  139.     {
  140.         if (addr < (*cc)->addr+(*cc)->size)
  141.             break;
  142.         else
  143.         {
  144.             if ((*cc)->next)
  145.                 cc = (*cc)->next;
  146.             else
  147.                 break;
  148.         }
  149.     }
  150.  
  151.     //LR - 970802 :fix possible crashes (also fixed load w/empty fork!)
  152.     if( !cc )
  153.         ErrorAlert( ES_Stop, "No chunks for window, unable to continue!" );
  154.  
  155.     return cc;
  156. }
  157.  
  158. short GetByte(EditWindowPtr dWin, long addr)
  159. {
  160.     register EditChunk **cc;
  161.     if ((cc = GetChunkByAddr(dWin,addr)) != NULL) {
  162.         // Correct Chunk
  163.         if (!(*cc)->loaded)
  164.             LoadChunk(dWin,cc);
  165.         if ((*cc)->lastCtr != dWin->useCtr) {
  166.             // Update the Counter
  167.             ++dWin->useCtr;
  168.             (*cc)->lastCtr = dWin->useCtr;
  169.         }
  170.         return (unsigned char) (*(*cc)->data)[addr - (*cc)->addr];
  171.     }
  172.     return -1;
  173. }
  174.  
  175. void LoadChunk(EditWindowPtr dWin, EditChunk **cc)
  176. {
  177.     long    count;
  178.     OSErr    oe;
  179.     short    refNum;
  180.     if ((*cc)->loaded)
  181.         return;
  182.     // Check if we can fit within MaxFileRam, if not, deallocate old chunks
  183.     // until we're ok
  184.     while (dWin->totLoaded+(*cc)->size > MaxFileRAM) {
  185.         UnloadLeastUsedChunk(dWin);
  186.     }
  187.     (*cc)->data = NewHandleClear((*cc)->size);
  188.     if ((*cc)->data == NULL) {
  189.         ErrorAlert(ES_Caution,"Not enough memory");
  190.         (*cc)->allocSize = 0L;
  191.         (*cc)->loaded = false;
  192.     }
  193.     else {
  194.         if ((*cc)->type == CT_Work)
  195.             refNum = dWin->workRefNum;
  196.         else
  197.             refNum = dWin->refNum;
  198.         (*cc)->allocSize = (*cc)->size;
  199.         (*cc)->loaded = true;
  200.         if ((oe = SetFPos(refNum, fsFromStart, (*cc)->filePos)) != noErr)
  201.             OSErrorAlert(ES_Caution,"Seek Error",oe);
  202.         count = (*cc)->size;
  203.         dWin->totLoaded += (*cc)->size;
  204.         if ((oe = FSRead(refNum, &count, *(*cc)->data)) != noErr)
  205.             OSErrorAlert(ES_Caution,"Read Error",oe);
  206.     }
  207. }
  208.  
  209. void UnloadLeastUsedChunk(EditWindowPtr dWin)
  210. {
  211.     EditChunk    **cc,**oc=NULL;
  212.     oc = cc = dWin->firstChunk;
  213.     while (cc) {
  214.         if ((*cc)->loaded && 
  215.             (!(*oc)->loaded || (*cc)->lastCtr < (*oc)->lastCtr))
  216.             oc = cc;
  217.         cc = (*cc)->next;
  218.     }
  219.     if (oc)
  220.         UnloadChunk(dWin, oc, true);
  221. }
  222.  
  223. void UnloadChunk(EditWindowPtr dWin, EditChunk    **cc, Boolean writeFlag)
  224. {
  225.     long    count;
  226.     OSErr    oe;
  227.  
  228.     if (cc && (*cc)->loaded && (*cc)->data) {
  229.         if (writeFlag && (*cc)->type == CT_Unwritten) {
  230.             // Record New Chunks in Work File
  231.             oe = SetFPos(dWin->workRefNum, fsFromStart, dWin->workBytesWritten);
  232.             if (oe) {
  233.                 OSErrorAlert(ES_Caution, "SetFPos", oe);
  234.             }
  235.             count = (*cc)->size;
  236.             oe = FSWrite(dWin->workRefNum, &count, *(*cc)->data);
  237.             if (oe) {
  238.                 OSErrorAlert(ES_Caution, "FSWrite", oe);
  239.             }
  240.             (*cc)->type = CT_Work;
  241.             (*cc)->filePos = dWin->workBytesWritten;
  242.             dWin->workBytesWritten += count;
  243.         }
  244.  
  245.         dWin->totLoaded -= (*cc)->size;
  246.         (*cc)->loaded = false;
  247.         DisposeHandle((*cc)->data);
  248.         (*cc)->data = NULL;
  249.         (*cc)->allocSize = 0L;
  250.     }
  251. }
  252.  
  253. void RewriteAddressChain(EditChunk **fc)
  254. {
  255.     EditChunk    **nc;
  256.     // Rewrite Addresses of chunks starting from fc
  257.     nc = (*fc)->next;
  258.     while (nc) {
  259.         (*nc)->addr = (*(*nc)->prev)->addr + (*(*nc)->prev)->size;
  260.         nc = (*nc)->next;
  261.     }
  262. }
  263.  
  264. void DeleteSelection(EditWindowPtr dWin)
  265. {
  266.     EditChunk **fc,**ec,**nc,**tc;
  267.  
  268.     if (dWin->endSel == dWin->startSel)
  269.         return;
  270.  
  271.     // Identify Starting Chunk
  272.     fc = GetChunkByAddr(dWin, dWin->startSel);
  273.     dWin->curChunk = fc;        // Optimize chunk searches
  274.  
  275.     // Identify Ending Chunk
  276.     ec = GetChunkByAddr(dWin, dWin->endSel);
  277.  
  278.     // If Chunks are the same
  279.     if (fc == ec) {
  280.         // If chunk is unwritten
  281.         if ((*fc)->type == CT_Unwritten) {
  282.             // Delete Chars from Buffer
  283.             // 12/14 JAB!!!  fixed editing bug
  284.             BlockMove(*(*fc)->data + (dWin->endSel - (*fc)->addr),
  285.                       *(*fc)->data + (dWin->startSel - (*fc)->addr),
  286.                       (*fc)->size - (dWin->endSel - (*fc)->addr));
  287. /*            BlockMove(*(*fc)->data + (dWin->endSel - (*fc)->addr),*/
  288. /*                      *(*fc)->data + (dWin->startSel - (*fc)->addr),*/
  289. /*                      dWin->endSel - dWin->startSel);*/
  290.             (*fc)->size -= dWin->endSel - dWin->startSel;
  291.         }
  292.         else {
  293.             UnloadChunk(dWin, fc, true);
  294.             // Split into two chunks
  295.             nc = NewChunk((*fc)->size - (dWin->endSel - (*fc)->addr), 
  296.                             0, 
  297.                             (*fc)->filePos + (dWin->endSel - (*fc)->addr), 
  298.                             (*fc)->type);
  299.             (*nc)->prev = fc;
  300.             (*nc)->next = (*fc)->next;
  301.             if ((*nc)->next)
  302.                 (*(*nc)->next)->prev = nc;
  303.             (*fc)->next = nc;
  304.             (*fc)->size = dWin->startSel - (*fc)->addr;
  305.         }
  306.     }
  307.     else {
  308.         // Truncate end of first Chunk
  309.         (*fc)->size = dWin->startSel - (*fc)->addr;
  310.         // Unlink & Dispose Middle Chunks, If Any
  311.         nc = (*fc)->next;
  312.         while (nc != ec) {
  313.             tc = (*nc)->next;
  314.             DisposeChunk(dWin, nc);
  315.             nc = tc;
  316.         }
  317.         (*ec)->prev = fc;
  318.         (*fc)->next = ec;
  319.         // Truncate beg of end chunk
  320.         if ((*ec)->type == CT_Unwritten) {
  321.             long    offset;
  322.             offset = dWin->endSel - (*ec)->addr;
  323.             BlockMove(*(*ec)->data, *(*ec)->data+offset, (*ec)->size - offset);
  324.             (*ec)->size -= offset;
  325.         }
  326.         else {
  327.             long    offset;
  328.             offset = dWin->endSel - (*ec)->addr;
  329.             UnloadChunk(dWin, ec, true);
  330.             (*ec)->filePos += offset;
  331.             (*ec)->size -= offset;
  332.         }
  333.     }
  334.  
  335.     dWin->fileSize -= (dWin->endSel - dWin->startSel);
  336.  
  337.     RewriteAddressChain(fc);
  338.  
  339.     // Modify Current Selection such that  endSel = firstSel
  340.     dWin->endSel = dWin->startSel;
  341.     dWin->dirtyFlag = true;
  342. }
  343.  
  344. // Assumes selection point is already 0 chars wide...
  345. void InsertCharacter(EditWindowPtr dWin, short charCode)
  346. {
  347.     EditChunk **fc,**ec,**nc;    //LR :remove tc to fix warnings
  348.  
  349.     // !! Remember Current State for Undo
  350.  
  351.  
  352.     // Insert Character Into List
  353.     //    Identify current chunk - optimize so that if char is between
  354.     //        chunks, pick the unwritten one of the two...
  355.  
  356.     // Identify Starting Chunk
  357.     fc = GetChunkByAddr(dWin, dWin->startSel);
  358.  
  359.     //    Identify current chunk - optimize so that if char is between
  360.     //    chunks, pick the unwritten one of the two... - this way, if I keep typing
  361.     //  characters, I won't generate a bunch of 1 byte chunks.
  362.     if (dWin->startSel - (*fc)->addr == 0 &&
  363.         (*fc)->prev && (*fc)->type != CT_Unwritten &&
  364.         (*(*fc)->prev)->type == CT_Unwritten) {
  365.         fc = (*fc)->prev;
  366.     }
  367.     dWin->curChunk = fc;        // Optimize chunk searches
  368.  
  369.     //    If current chunk is not unwritten
  370.     if ((*fc)->type != CT_Unwritten) {
  371.         // Unload it
  372.         UnloadChunk(dWin, fc, true);
  373.  
  374.         if (dWin->startSel > (*fc)->addr) {
  375.  
  376.             // Split into two chunks
  377.             if (dWin->startSel < (*fc)->addr + (*fc)->size) {
  378.                 ec = NewChunk((*fc)->size - (dWin->startSel - (*fc)->addr), 
  379.                                 0, 
  380.                                 (*fc)->filePos + (dWin->startSel - (*fc)->addr), 
  381.                                 (*fc)->type);
  382.                 (*ec)->prev = fc;
  383.                 (*ec)->next = (*fc)->next;
  384.                 if ((*ec)->next)
  385.                     (*(*ec)->next)->prev = ec;
  386.                 (*fc)->next = ec;
  387.             }
  388.             else
  389.                 ec = (*fc)->next;
  390.  
  391.             (*fc)->size = dWin->startSel - (*fc)->addr;
  392.         }
  393.         else {
  394.             ec = fc;
  395.             fc = (*fc)->prev;
  396.         }
  397.  
  398.         // Add New unwritten chunk in middle with 0 size
  399.         nc = NewChunk(0,0,0,CT_Unwritten);
  400.         if (fc) {
  401.             (*fc)->next = nc;
  402.             (*nc)->addr = (*fc)->addr + (*fc)->size;
  403.         }
  404.         else
  405.             dWin->firstChunk = nc;
  406.         if (ec)
  407.             (*ec)->prev = nc;
  408.         (*nc)->prev = fc;
  409.         (*nc)->next = ec;
  410.         // current chunk = new chunk
  411.         dWin->curChunk = nc;
  412.         fc = nc;
  413.     }
  414.  
  415.     //    Expand Ptr if Necessary
  416.     if ((*fc)->allocSize <= (*fc)->size) {
  417.         (*fc)->allocSize += AllocIncr;        // !! consider expanding as size goes up
  418.         SetHandleSize((*fc)->data,(*fc)->allocSize);
  419.     }
  420.  
  421.     // Make Room for Character if necessary
  422.     if (dWin->startSel < (*fc)->addr + (*fc)->size)
  423.         BlockMove(*(*fc)->data + (dWin->startSel - (*fc)->addr), 
  424.                   *(*fc)->data + (1+(dWin->startSel - (*fc)->addr)), 
  425.                   (*fc)->addr + (*fc)->size - dWin->startSel);
  426.  
  427.     //    Insert Char into buffer
  428.     (*(*fc)->data)[dWin->startSel - (*fc)->addr] = charCode;
  429.  
  430.     //    Update Fields in this chunk
  431.     (*fc)->size++;
  432.     dWin->fileSize++;
  433.  
  434.     // Set Dirty Flag
  435.     dWin->dirtyFlag = true;
  436.  
  437.     //    Update addr fields of following chunks
  438.     RewriteAddressChain(fc);
  439.  
  440.     // Increment current Selection
  441.     dWin->startSel++;
  442.     dWin->endSel++;
  443.  
  444.  
  445.     // Update Display
  446.     ScrollToSelection(dWin,dWin->startSel,true, false);
  447. }
  448.  
  449. void ReleaseEditScrap(EditWindowPtr dWin, EditChunk ***scrap)
  450. {
  451.     EditChunk    **cc,**bc;
  452.     cc = *scrap;
  453.     while (cc) {
  454.         bc = (*cc)->next;
  455.         DisposeChunk(dWin, cc);
  456.         cc = bc;
  457.     }
  458.     *scrap = NULL;
  459. }
  460.  
  461. // High Level Copy
  462. void CopySelection(EditWindowPtr dWin)
  463. {
  464.     PScrapStuff ScrapInfo;    //LR :see below
  465.  
  466.     CopyOperation(dWin, &gScrapChunk);
  467.     if (gScrapChunk) {
  468.         // Copy to Desk Scrap
  469.         ZeroScrap();
  470.         HLock((*gScrapChunk)->data);
  471.         PutScrap((*gScrapChunk)->size, 'TEXT', *(*gScrapChunk)->data);
  472.         HUnlock((*gScrapChunk)->data);
  473.  
  474.         //LR :the "correct" way to do things (UniversalHeaders)
  475.         ScrapInfo = InfoScrap();
  476.         gScrapCount = ScrapInfo->scrapCount;
  477. //        gScrapCount = ScrapInfo.scrapCount;
  478.  
  479.         (*gScrapChunk)->lastCtr = 0;    // Flag as internal
  480.     }
  481. }
  482.  
  483.  
  484.  
  485. void CopyOperation(EditWindowPtr dWin, EditChunk ***scrapChunk)
  486. {
  487.     EditChunk    **fc,**ec,**nc,**tc;
  488.     // Unload current scrap
  489.     ReleaseEditScrap(dWin, scrapChunk);
  490.  
  491.     // Copy current selection into scrapChunk
  492.     // Identify Starting Chunk
  493.     fc = GetChunkByAddr(dWin, dWin->startSel);
  494.     dWin->curChunk = fc;        // Optimize chunk searches
  495.  
  496.     // Identify Ending Chunk
  497.     ec = GetChunkByAddr(dWin, dWin->endSel);
  498.  
  499.     // If Chunks are the same
  500.     nc = NewChunk(dWin->endSel - dWin->startSel,
  501.                   0,
  502.                   0,
  503.                   CT_Unwritten);
  504.     if (nc == NULL)
  505.         return;
  506.  
  507.     *scrapChunk = nc;
  508.  
  509.     if (fc == ec) {
  510.         LoadChunk(dWin, fc);
  511.         BlockMove(*(*fc)->data + (dWin->startSel - (*fc)->addr),
  512.                   *(*nc)->data,
  513.                   (*nc)->size);
  514.     }
  515.     else {
  516.         // First Chunk to End
  517.         tc = fc;
  518.         LoadChunk(dWin, tc);
  519.         BlockMove(*(*tc)->data + (dWin->startSel - (*tc)->addr),
  520.                   *(*nc)->data,
  521.                   (*tc)->size - (dWin->startSel - (*tc)->addr));
  522.         tc = (*tc)->next;
  523.  
  524.         // Middle Chunks, If Any
  525.         while (tc != ec) {
  526.             LoadChunk(dWin, tc);
  527.             BlockMove(*(*tc)->data,
  528.                       *(*nc)->data + ((*tc)->addr - dWin->startSel),
  529.                       (*tc)->size);
  530.             tc = (*tc)->next;
  531.         }
  532.  
  533.         // Last Chunk
  534.         LoadChunk(dWin, tc);
  535.         BlockMove(*(*tc)->data,
  536.                   *(*nc)->data + ((*tc)->addr - dWin->startSel),
  537.                   dWin->endSel - (*tc)->addr);
  538.     }
  539. }
  540.  
  541. void CutSelection(EditWindowPtr dWin)
  542. {
  543.     RememberOperation(dWin, EO_Cut, &gUndoRec);
  544.     CopyOperation(dWin, &gScrapChunk);        // Copy into paste buffer
  545.     DeleteSelection(dWin);
  546.     dWin->dirtyFlag = true;
  547.     ScrollToSelection(dWin,dWin->startSel,true, false);
  548. }
  549.  
  550. // High Level Paste
  551. void PasteSelection(EditWindowPtr dWin)
  552. {
  553.     RememberOperation(dWin, EO_Paste, &gUndoRec);
  554.     PasteOperation(dWin, gScrapChunk);
  555.     dWin->dirtyFlag = true;
  556.     ScrollToSelection(dWin,dWin->startSel,true, false);
  557. }
  558.  
  559. Boolean HexConvertScrap(EditWindowPtr dWin, EditChunk **scrapChunk)
  560. {
  561. #pragma unused (dWin)    //LR :fix warnings
  562.  
  563.     Handle    rh=NULL;
  564.     Ptr        sp,dp,esp;
  565.     short    val;
  566.     Boolean    loFlag;
  567.  
  568.     rh = NewHandle((*scrapChunk)->size);
  569.     if (rh == NULL) {
  570.         ErrorAlert(ES_Caution, "Not enough memory");
  571.         return false;
  572.     }
  573.     HLock(rh);
  574.     HLock((*scrapChunk)->data);
  575.     sp = *(*scrapChunk)->data;
  576.     esp = sp + (*scrapChunk)->size;
  577.     dp = *rh;
  578.     loFlag = false;
  579.     for (; sp < esp; ++sp) {
  580.         if (*sp == '0' && *(sp+1) == 'x') {
  581.             loFlag = 0;
  582.             ++sp;
  583.             continue;
  584.         }
  585.         if( isspace(*sp) || ispunct(*sp)) {
  586.             loFlag = 0;
  587.             continue;
  588.         }
  589.         if (*sp >= '0' && *sp <= '9')
  590.             val = *sp - '0';
  591.         else if (*sp >= 'A' && *sp <= 'F')
  592.             val = 0x0A + (*sp - 'A');
  593.         else if (*sp >= 'a' && *sp <= 'f')
  594.             val = 0x0A + (*sp - 'a');
  595.         else
  596.             goto HexError;
  597.         if (loFlag) {
  598.             *(dp-1) = (*(dp-1) << 4) | val;
  599.             loFlag = 0;
  600.         }            
  601.         else {
  602.             *dp = val;
  603.             ++dp;
  604.             loFlag = 1;
  605.         }
  606.     }
  607.     if (dp - *rh == 0)
  608.         goto HexError;
  609.     (*scrapChunk)->size = dp - *rh;
  610.     HUnlock(rh);
  611.     HUnlock((*scrapChunk)->data);
  612.     BlockMove(*rh, *(*scrapChunk)->data, (*scrapChunk)->size);
  613.     DisposeHandle(rh);
  614.     (*scrapChunk)->lastCtr = 0;        // Mark as Internal
  615.     return true;
  616. HexError:
  617.     HUnlock(rh);
  618.     HUnlock((*scrapChunk)->data);
  619.     ErrorAlert(ES_Caution, "Only valid Hex values may be pasted here");
  620.     DisposeHandle(rh);
  621.     return false;
  622. }
  623.  
  624. void PasteOperation(EditWindowPtr dWin, EditChunk **scrapChunk)
  625. {
  626.     EditChunk **fc,**ec,**nc;
  627.  
  628.     // Hex Pasting Mode for Outside Pastes
  629.     if (dWin->editMode == EM_Hex && (*gScrapChunk)->lastCtr == 1) {
  630.         if (!HexConvertScrap(dWin,scrapChunk))
  631.             return;
  632.     }
  633.  
  634.     // Create duplicate scrap attached to nc->nec
  635.     nc = NewChunk((*scrapChunk)->size,
  636.                     0,
  637.                     0,
  638.                     CT_Unwritten);
  639.     if (nc == NULL)
  640.         return;
  641.  
  642.     BlockMove(*(*scrapChunk)->data,
  643.               *(*nc)->data,
  644.               (*nc)->size);
  645.  
  646.     DeleteSelection(dWin);
  647.     // Insert paste buffer into selStart
  648.  
  649.     fc = GetChunkByAddr(dWin, dWin->startSel);
  650.     if ((*fc)->addr < dWin->startSel) {
  651.         // Split 'em up
  652.         // Unload it
  653.         UnloadChunk(dWin, fc, true);
  654.  
  655.         // Split into two chunks
  656.         if (dWin->startSel < (*fc)->addr + (*fc)->size) {
  657.             ec = NewChunk((*fc)->size - (dWin->startSel - (*fc)->addr), 
  658.                             0, 
  659.                             (*fc)->filePos + (dWin->startSel - (*fc)->addr), 
  660.                             (*fc)->type);
  661.             (*ec)->prev = fc;
  662.             (*ec)->next = (*fc)->next;
  663.             if ((*ec)->next)
  664.                 (*(*ec)->next)->prev = ec;
  665.         }
  666.         else
  667.             ec = (*fc)->next;
  668.  
  669.         (*fc)->next = ec;
  670.         (*fc)->size = dWin->startSel - (*fc)->addr;
  671.     }
  672.     else {
  673.         ec = fc;
  674.         fc = (*fc)->prev;
  675.     }
  676.  
  677.     // Insert fc->nc->ec
  678.     if (fc) {
  679.         (*fc)->next = nc;
  680.         (*nc)->prev = fc;
  681.         (*nc)->addr = (*fc)->addr + (*fc)->size;
  682.     }
  683.     else {
  684.         dWin->firstChunk = nc;
  685.         (*nc)->addr = 0L;
  686.     }
  687.  
  688.     if (ec) {
  689.         (*nc)->next = ec;
  690.         (*ec)->prev = nc;
  691.     }
  692.  
  693.     // Correct addresses
  694.     RewriteAddressChain(nc);
  695.  
  696.     // Reset Selection
  697.     dWin->startSel = dWin->endSel = (*nc)->addr + (*nc)->size;
  698.  
  699.     // Update other stuff
  700.     dWin->fileSize += (*scrapChunk)->size;
  701.     dWin->dirtyFlag = true;
  702. }
  703.  
  704. void ClearSelection(EditWindowPtr dWin)
  705. {
  706.     RememberOperation(dWin, EO_Clear, &gUndoRec);
  707.     DeleteSelection(dWin);
  708.     dWin->dirtyFlag = true;
  709.     ScrollToSelection(dWin, dWin->startSel, true, false);
  710. }
  711.  
  712. // Remember current state for Undo of following operation
  713. void RememberOperation(EditWindowPtr dWin, short opType, UndoRecord *ur)
  714. {
  715.     // Forget Last stuff
  716.     if (ur == &gRedoRec) {
  717.         // Reset menu text to Redo
  718.         Str31    menuStr;
  719.         GetMenuItemText(gEditMenu, EM_Undo, menuStr);
  720.         if (menuStr[1] == 'R')
  721.             BlockMove("Un", &menuStr[1], 2);
  722.         else
  723.             BlockMove("Re", &menuStr[1], 2);
  724.         SetMenuItemText(gEditMenu, EM_Undo, menuStr);
  725.     }
  726.     else {
  727.         Str31    undoStr;
  728.         Str31    menuStr;
  729.         GetIndString(undoStr, UndoSTRs, opType);
  730.         BlockMove("\pUndo ", menuStr, 6);
  731.         BlockMove(&undoStr[1], &menuStr[6], undoStr[0]);
  732.         menuStr[0] += undoStr[0];
  733.         SetMenuItemText(gEditMenu, EM_Undo, menuStr);
  734.     }
  735.  
  736.     ReleaseEditScrap(dWin, &ur->undoScrap);
  737.     // Clear Undo Stuff
  738.     ur->undoScrap = NULL;
  739.     ur->type = opType;
  740.     ur->startSel = dWin->startSel;
  741.     ur->endSel = dWin->endSel;
  742.     ur->fileSize = dWin->fileSize;
  743.     ur->window = dWin;
  744.     CopyOperation(dWin, &ur->undoScrap);
  745.     (*ur->undoScrap)->lastCtr= 0;
  746.     dWin->lastTypePos = -1;    // Clear Special Editing Modes
  747.     dWin->loByteFlag = false;
  748. }
  749.  
  750. void UndoOperation( void )
  751. {
  752.     EditWindowPtr    dWin = gUndoRec.window;
  753.  
  754.     if (gUndoRec.type == 0)
  755.         return;
  756.     if (dWin != (EditWindowPtr) FrontWindow())
  757.         SelectWindow((WindowPtr) dWin);
  758.  
  759.     switch (gUndoRec.type) {
  760.     case EO_Typing:
  761.     case EO_Paste:
  762.     case EO_Insert:
  763.         dWin->startSel = gUndoRec.startSel;
  764.         dWin->endSel = dWin->fileSize - (gUndoRec.fileSize - gUndoRec.endSel);
  765.         RememberOperation(dWin, EO_Delete, &gRedoRec);
  766.         DeleteSelection(dWin);
  767.         PasteOperation(dWin, gUndoRec.undoScrap);
  768.         break;
  769.     case EO_Cut:
  770.     case EO_Clear:
  771.     case EO_Delete:
  772.         dWin->startSel = dWin->endSel = gUndoRec.startSel;
  773.         RememberOperation(dWin, EO_Insert, &gRedoRec);
  774.         PasteOperation(dWin, gUndoRec.undoScrap);
  775.         break;
  776.     }
  777.  
  778.     ReleaseEditScrap(dWin, &gUndoRec.undoScrap);
  779.     gUndoRec = gRedoRec;
  780.     gRedoRec.undoScrap = NULL;
  781.  
  782.     ScrollToSelection(dWin,dWin->startSel,true, false);
  783. }
  784.  
  785.